/**************************************************************************** 
 * 
 * Copyright (c) 2023  C*Core -   All Rights Reserved  
 * 
 * THIS SOFTWARE IS DISTRIBUTED "AS IS," AND ALL WARRANTIES ARE DISCLAIMED, 
 * INCLUDING MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 * 
 * PROJECT     : CCFC2011BC           
 * DESCRIPTION : CCFC2011BC stm low level drivers code 
 * HISTORY     : Initial version.
 * @file     stm_lld.c
 * @version  1.1
 * @date     2023 - 02 - 20
 * @brief    Initial version.
 * 
 *****************************************************************************/
#include "stm_lld.h"
#include "IntcInterrupts.h"
#include "sys.h"

STMDriver STMD;

/*===========================================================================*/
/* Driver exported functions.                                                */
/*===========================================================================*/

/*******************************************************************************
 * @brief      STM0 CHANNEL 0 interrupt handler.
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi     @isr
 *******************************************************************************/
static IRQ_HANDLER(STM0_CH0_HANDLER)
{
    IRQ_PROLOGUE();
    /* call related callback */
    if (STMD.stm_ch_config[0].callback != NULL )
    {
        STMD.stm_ch_config[0].callback();
    }
    STMD.stm_tagp->CH[0].CIR.B.CIF = STM_CCR_CIF_CLEAR;
    IRQ_EPILOGUE();
}

static IRQ_HANDLER(STM0_CH1_HANDLER)
{
    IRQ_PROLOGUE();
    /* call related callback */
    if (STMD.stm_ch_config[1].callback != NULL )
    {
        STMD.stm_ch_config[1].callback();
    }
    STMD.stm_tagp->CH[1].CIR.B.CIF = STM_CCR_CIF_CLEAR;
    IRQ_EPILOGUE();
}

static IRQ_HANDLER(STM0_CH2_HANDLER)
{
    IRQ_PROLOGUE();
    /* call related callback */
    if (STMD.stm_ch_config[2].callback != NULL )
    {
        STMD.stm_ch_config[2].callback();
    }
    STMD.stm_tagp->CH[2].CIR.B.CIF = STM_CCR_CIF_CLEAR;
    IRQ_EPILOGUE();
}

static IRQ_HANDLER(STM0_CH3_HANDLER)
{
    IRQ_PROLOGUE();
    /* call related callback */
    if (STMD.stm_ch_config[3].callback != NULL )
    {
        STMD.stm_ch_config[3].callback();
    }
    STMD.stm_tagp->CH[3].CIR.B.CIF = STM_CCR_CIF_CLEAR;
    IRQ_EPILOGUE();
}

/*******************************************************************************
 * @brief      Low level stm driver initialization.
 * @param[in]  stmd      pointer to the @p STMDriver object
 *             config
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void STM_LLD_Init(STMDriver *stmd, STM_CH_Config *config)
{
    /* Install interrupt handler for STM*/
#if (STM_USE_STM0 == TRUE)
    STM_LLD_DeInit(stmd);
#endif
#if ((STM0_CH0_ENABLED == TRUE)|| (STM_USE_STM0_INT == TRUE))
    INTC_InstallINTCInterruptHandler(STM0_CH0_HANDLER, STM0_CH0_INT_NUMBER,
            STM0_CH0_IRQ_PRIO);
#endif
#if ((STM0_CH1_ENABLED == TRUE)|| (STM_USE_STM0_INT == TRUE))
    INTC_InstallINTCInterruptHandler(STM0_CH1_HANDLER, STM0_CH1_INT_NUMBER,
            STM0_CH1_IRQ_PRIO);
#endif
#if ((STM0_CH2_ENABLED == TRUE)|| (STM_USE_STM0_INT == TRUE))
    INTC_InstallINTCInterruptHandler(STM0_CH2_HANDLER, STM0_CH2_INT_NUMBER,
            STM0_CH2_IRQ_PRIO);
#endif
#if ((STM0_CH3_ENABLED == TRUE)|| (STM_USE_STM0_INT == TRUE))
    INTC_InstallINTCInterruptHandler(STM0_CH3_HANDLER, STM0_CH3_INT_NUMBER,
            STM0_CH3_IRQ_PRIO);
#endif
    /* Set Driver configuration */
    stmd->stm_ch_config = config;
}

/*******************************************************************************
 * @brief      Enable Global Counter for STMD driver and activate channels 
 * @param[in]  stmd      pointer to the @p STMDriver object
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void STM_LLD_ModuleEnable(const STMDriver *stmd)
{
    uint8_t i;
    /* set the prescaler */
    stmd->stm_tagp->CR.R = (stmd->prescaler - 1U) << 8;
    /* set compare register and enable channels */
    for (i = 0; i < stmd->noofchannels; i++)
    {
        if (stmd->stm_ch_config[i].enabled == TRUE)
        {
            /* set compare register */
            stmd->stm_tagp->CH[i].CMP.B.CMP = stmd->stm_ch_config[i].counter;
            /* clear interrupt flag */
            stmd->stm_tagp->CH[i].CIR.B.CIF = STM_CCR_CIF_CLEAR;
            /* enable channel */
            stmd->stm_tagp->CH[i].CCR.B.CEN = STM_CIR_CEN_ENABLE;
        }
    }
    /* reset counter */
    stmd->stm_tagp->CNT.R = 0x00000000U;
    /* Freeze in debug */
    stmd->stm_tagp->CR.B.FRZ = STM_CR_FRZ_ENABLE;
    /*enable STM */
    stmd->stm_tagp->CR.B.TEN = STM_CR_TEN_ENABLE;
}

/*******************************************************************************
 * @brief      Disable Global Counter for STMD driver and deactivate channels
 * @param[in]  stmd      pointer to the @p STMDriver object
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void STM_LLD_ModuleDisable(const STMDriver *stmd)/* PRQA S 1505*/
{
    uint8_t i;
    /* set compare register and enable channels */
    for (i = 0; i < stmd->noofchannels; i++)
    {
        if (stmd->stm_ch_config[i].enabled == TRUE)
        {
            /* set compare registers to 0 */
            stmd->stm_tagp->CH[i].CMP.B.CMP = 0x0000000U;
            /* clear  interrupt flag */
            stmd->stm_tagp->CH[i].CIR.B.CIF = STM_CCR_CIF_CLEAR;
            /* disable channel */
            stmd->stm_tagp->CH[i].CCR.B.CEN = STM_CIR_CEN_DISABLE;
        }
    }
    /*Disable STM */
    stmd->stm_tagp->CR.B.TEN = STM_CR_TEN_DISABLE;
}

/*******************************************************************************
 * @brief      STM_LLD_DeInit
 * @param[in]  stmd      pointer to the @p STMDriver object
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void STM_LLD_DeInit(STMDriver *stmd)
{
    if (stmd->stm_tagp->CR.B.TEN == STM_CR_TEN_ENABLE)
    {
        STM_LLD_ModuleDisable(stmd);
    }
    /* Clear configuration structure */
    stmd->stm_ch_config = NULL;
}

/*******************************************************************************
 * @brief      Activate and configure STM peripheral
 * @param[in]  stmd      pointer to the @p STMDriver object
 *             config   pointer to the @p STM_CH_Config object
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void STM_LLD_Configure(STMDriver *stmd)
{
#if (STM_USE_STM0 == TRUE)
    if (&STMD == stmd)
    {
        /* Set module prescaler */
        stmd->prescaler = STM0_SYSCLOCK_PRE;
        /* Set pointer to the STMDriver object */
        stmd->stm_tagp = &STM;
        /* Set module channel */
        stmd->noofchannels = STM_CHANNELS;
    }
#endif
}

/*******************************************************************************
 * @brief      STOP the STM Driver
 * @param[in]  stmd      pointer to the @p STMDriver object
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void STM_LLD_Stop(const STMDriver *stmd)
{
    if (stmd->stm_tagp->CR.B.TEN == STM_CR_TEN_ENABLE)
    {
        /*Disable STM */
        STM_LLD_ModuleDisable(stmd);
    }
}

/*******************************************************************************
 * @brief      Counter Reset of STM Driver
 * @param[in]  stmd      pointer to the @p STMDriver object
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void STM_LLD_ResetCounter(const STMDriver *stmd)
{
    /*Set count value 0x00000000*/
    STM_LLD_SetCounter(stmd, 0x00000000U);
}

/*******************************************************************************
 * @brief      Get Counter value of STM Driver
 * @param[in]  tmd      pointer to the @p STMDriver objec
 * @param[out] None
 * @retval     current counter value 
 * @notapi
 *******************************************************************************/
uint32_t STM_LLD_GetCounter(const STMDriver *stmd)
{
    /*Get count value*/
    return (stmd->stm_tagp->CNT.R);
}

/*******************************************************************************
 * @brief      Set counter value of STM Driver
 * @param[in]  stmd      pointer to the @p STMDriver object
 *             value     value of counter to be set
 * @param[out] None
 * @retval     None 
 * @notapi
 *******************************************************************************/
void STM_LLD_SetCounter(const STMDriver *stmd, uint32_t value)/* PRQA S 1505*/
{
    /*disable counter */
    stmd->stm_tagp->CR.B.TEN = STM_CR_TEN_DISABLE;
    /* reset counter */
    stmd->stm_tagp->CNT.R = value;
    /*enable counter */
    stmd->stm_tagp->CR.B.TEN = STM_CR_TEN_ENABLE;
}

/*******************************************************************************
 * @brief      STM delay initialization. 
 *             1.Initialize timer 
 *             2.Configure timer 
 *             3.Enable timer
 *             When using STM delay, the timer value will often return to zero, 
 *             so do not use timer delay and other functions of timer at the
 *             same time
 * @param[in]  None
 * @param[out] None
 * @retval     None 
 * @notapi
 *******************************************************************************/
void STM_LLD_DelayInit(void) /*if sys_clk < 1000000 ,error*/
{
    uint32_t t_prescaler;
    /* Set pointer to the STMDriver object */
    STMD.stm_tagp = &STM;
    /* Set prescaler to make module clock 1M */
    t_prescaler = GetSystemClock()/1000000u;
    STM_LLD_SetPrescaler(t_prescaler);
    /* Enable STM */
    STM.CR.B.TEN = 1;
}

/*******************************************************************************
 * @brief      delay by STM Unit: milliseconds
 * @param[in]  ms     the value to delay
 * @param[out] None
 * @retval     None 
 * @notapi
 *******************************************************************************/
void STM_LLD_Delayms(uint32_t ms)
{
    STM_LLD_ResetCounter(&STMD);
    /*Frequency 1M,1000 clock = 1ms*/
    while ((STM.CNT.R) < (ms*1000u))
    {}
}

/*******************************************************************************
 * @brief      delay by STM Unit: microseconds
 * @param[in]  us     the value to delay
 * @param[out] None
 * @retval     None 
 * @notapi
 *******************************************************************************/
void STM_LLD_Delayus(uint32_t us)
{
    STM_LLD_ResetCounter(&STMD);
    /*Frequency 1M,1 clock = 1us*/
    while (STM.CNT.R < us)
    {}
}

/*******************************************************************************
 * @brief      set STM counter prescaler
 * @param[in]  ClockDivision     Counter Prescaler(1~256)
 * @param[out] None
 * @retval     None 
 * @notapi
 *******************************************************************************/
void STM_LLD_SetPrescaler(uint32_t clockDivision)
{
    /* Set STM counter prescaler */
    STM.CR.B.CPS = clockDivision - 1u;
}

/*******************************************************************************
 * @brief      get STM counter prescaler
 * @param[in]  None
 * @param[out] ClockDivision     Counter Prescaler(1~256)
 * @retval     None 
 * @notapi
 *******************************************************************************/
uint32_t STM_LLD_GetPrescaler(void)
{
    /* Get STM counter prescaler */
    return (uint32_t)STM.CR.B.CPS + 1u;
}

/*******************************************************************************
 * @brief      Allows the timer counter to be stopped when the device enters
               debug mode.
 * @param[in]  NewState     ENABLE or DISABLE
 * @param[out] None
 * @retval     None 
 * @notapi
 *******************************************************************************/
void STM_LLD_SetDebugFreeze(uint32_t newState)
{
    /* Set Freeze mode */
    STM.CR.B.FRZ = newState;
}

/*******************************************************************************
 * @brief      Allows the timer counter to be stopped when the device enters debug mode.
 * @param[in]  None
 * @param[out] 0 = STM counter continues to run in debug mode
 *             1 = STM counter is stopped in debug mode.  
 * @retval     None 
 * @notapi
 *******************************************************************************/
uint32_t STM_LLD_GetDebugFreeze(void)
{
    /* Get Freeze mode state */
    return STM.CR.B.FRZ;
}


/*******************************************************************************
 * @brief      STM channel enable
 * @param[in]  Channel      Channels to be enabled
 *             NewState New state of the channel    ENABLE or DISABLE
 * @param[out] None
 * @retval     None 
 * @notapi
 *******************************************************************************/
void STM_LLD_SetChannelState(uint32_t channel , uint32_t newState)
{
    /* Set STM channel enable */
    STM.CH[channel].CCR.B.CEN = newState;
}

/*******************************************************************************
 * @brief      get STM channel state
 * @param[in]  Channels to be enabled
 * @param[out] channel state    0:disable 1:enable
 * @retval     None 
 * @notapi
 *******************************************************************************/
uint32_t STM_LLD_GetChannelState(uint32_t channel)
{
    /* Get STM channel enable */
    return STM.CH[channel].CCR.B.CEN;
}


/*******************************************************************************
 * @brief      clear interrupt flag
 * @param[in]  Channel      Channels to be cleared
 * @param[out] None
 * @retval     None 
 * @notapi
 *******************************************************************************/
void STM_LLD_ClearInterruptFlag(uint32_t channel)
{
    /* Clear interrupt flag */
    STM.CH[channel].CIR.B.CIF = 1;
}

/*******************************************************************************
 * @brief      get interrupt flag
 * @param[in]  Channels to be cleared
 * @param[out] None
 * @retval     0:No interrupt request. 
 *             1:Interrupt request due to a match on the channel
 * @notapi
 *******************************************************************************/
uint32_t STM_LLD_GetInterruptFlag(uint32_t channel)
{
    /* Get interrupt flag */
    return STM.CH[channel].CIR.B.CIF;
}

/*******************************************************************************
 * @brief      set compare value
 * @param[in]  Channel          Channels to be selected
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void STM_LLD_SetCompareValue(uint32_t channel , uint32_t compareValue)
{
    /* Set channel compare value */
    STM.CH[channel].CMP.B.CMP = compareValue;
}

/*******************************************************************************
 * @brief      get compare value
 * @param[in]  Channel          Channels to be selected
 * @param[out] Compare value for channel
 * @retval     None
 * @notapi
 *******************************************************************************/
uint32_t STM_LLD_GetCompareValue(uint32_t channel)
{
    /* Get channel compare value */
    return STM.CH[channel].CMP.B.CMP;
}


